﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics.Tracing;
using System.Drawing;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using OpenCvSharp;
using OpenCvSharp.Extensions;

namespace TemplateMatch
{
    public partial class CreateTemplates : Form
    {
        private string ImagePath;
        List<Rect> BoundingBoxes;
        public CreateTemplates(string imagePath)
        {
            InitializeComponent();
            ImagePath = imagePath;
        }

        private void CreateTemplates_Load(object sender, EventArgs e)
        {
            imageBox1.Image = Image.FromFile(ImagePath);
        }

        private void image_show(string title, Mat ImageToShow)
        {
            Cv2.NamedWindow(title, WindowMode.Normal);
            Cv2.ImShow(title, ImageToShow);
            Cv2.WaitKey(0);
            Cv2.DestroyAllWindows();
        }

        private Mat ProcessImage(Mat soureImage)    //pre process
        {
            Mat dest = new Mat();
            

            //convert to gray
            Cv2.CvtColor(soureImage, dest, ColorConversionCodes.BGR2GRAY);
            //black and white
            Cv2.Threshold(dest, dest, 127, 255, ThresholdTypes.BinaryInv);

            //Cv2.Resize(dest, dest, new OpenCvSharp.Size(dest.Width * 5, dest.Height * 5));

            Mat kernel = Cv2.GetStructuringElement(MorphShapes.Rect, new OpenCvSharp.Size(3, 3));
            //tophat
            //Cv2.MorphologyEx(dest, dest, MorphTypes.TopHat, kernel);
            //Close
            Cv2.MorphologyEx(dest, dest, MorphTypes.Close, kernel);
            Cv2.MorphologyEx(dest, dest, MorphTypes.Close, kernel);
            Cv2.MorphologyEx(dest, dest, MorphTypes.Close, kernel);
            Cv2.MorphologyEx(dest, dest, MorphTypes.Close, kernel);
            Cv2.MorphologyEx(dest, dest, MorphTypes.Close, kernel);
            //Canny
            //Cv2.Canny(dest, dest, 50, 150);

            //image_show("Processed", dest.ToBitmap());

            return dest;
        }

        private void btnShow_Click(object sender, EventArgs e)
        {
            Mat sourceImage = Cv2.ImRead(ImagePath);            
            Mat destImage = ProcessImage(sourceImage);


            //identify contours
            BoundingBoxes = GetBoundingBoxes(destImage);
            //draw bounding boxes on drawing
            foreach (Rect box in BoundingBoxes)
            {
                if (box.Width < 3000)
                {
                    sourceImage.Rectangle(box, Scalar.Green);
                    sourceImage.PutText(string.Format("{0} X {1}", box.Width.ToString(), box.Height.ToString()), new OpenCvSharp.Point(box.X, box.Y), HersheyFonts.HersheySimplex, 0.25, Scalar.Green);
                }
            }
            //image_show("Bounding Boxes", sourceImage.ToBitmap());
            imageBox1.Image = sourceImage.ToBitmap();
            sourceImage.Dispose();
            destImage.Dispose();
        }

        private void btnCreate_Click(object sender, EventArgs e)
        {
            if (BoundingBoxes is null)
            {
                MessageBox.Show("No bounding boxes found!");
                return;
            }

            if (BoundingBoxes.Count == 0)
            {
                MessageBox.Show("No bounding boxes found!");
                return;
            }

            Mat sourceImage = Cv2.ImRead(ImagePath);
            
            List<Template> templates = new List<Template>();
            for(int i = 0; i < BoundingBoxes.Count; i++)
            {
                Template t = new Template(i);
                t.SetImage(sourceImage[BoundingBoxes[i]].ToBitmap());
                templates.Add(t);
            }

            dlgSaveTemplate dlg = new dlgSaveTemplate(templates);
            dlg.ShowDialog();
        }

        private List<Rect> GetBoundingBoxes(Mat sourceImage)    //find contours and get bounding boxes of each symbol
        {
            //Cv2.Resize(sourceImage, sourceImage, new OpenCvSharp.Size(sourceImage.Width * 5, sourceImage.Height * 5));
            
            List<Rect> BoundingBoxes = new List<Rect>();
            OpenCvSharp.Point[][] contours;
            OpenCvSharp.HierarchyIndex[] hierarchies;

            Cv2.FindContours(sourceImage, out contours, out hierarchies, RetrievalModes.External, ContourApproximationModes.ApproxSimple);
            Cv2.DrawContours(sourceImage, contours, -1, Scalar.Green);
            //image_show("Contours", sourceImage.ToBitmap());
            
            foreach(OpenCvSharp.Point[] contour in contours)
            {
                Rect box = Cv2.BoundingRect(contour);
                //box.Inflate(Convert.ToInt32(box.Width / 5), Convert.ToInt32(box.Height / 5));
                BoundingBoxes.Add(box);
            }

            return BoundingBoxes;
        }

        private Mat RemovedGrids(Mat sourceImage)
        {
            Mat dest = new Mat();
            sourceImage.CopyTo(dest);

            LineSegmentPoint[] lines = Cv2.HoughLinesP(sourceImage, 10, 2, 200, 10, 2);

            foreach(LineSegmentPoint line in lines)
            {
                Cv2.Line(dest, line.P1, line.P2, Scalar.Red);
            }

            image_show("Lines", dest.ToBitmap());
            return dest;
        }

        private void image_show(string title, Image imageToShow)
        {
            dlgImageShow dlg = new dlgImageShow(imageToShow);
            dlg.Text = title;
            dlg.Show();
        }
    }
}
